using System;
using System.Collections.Generic;

	public abstract class Condition<MapType>{
		public abstract bool match(MapType from);
	}

	public abstract class Rule<MapType>{
		Condition<MapType> condition;
		Rule<MapType> chainedRule;
		public Condition<MapType> Condition {
			get {
				return this.condition;
			}
			set {
				condition = value;
			}
		}

		public Rule<MapType> ChainedRule {
			get {
				return this.chainedRule;
			}
			set {
				chainedRule = value;
			}
		}
		public MapType mapChained(MapType from){
			if( ChainedRule==null || condition.match(from) ){
				return map (from);
			}
			return chainedRule.mapChained(from);
		}
		public virtual void addCorrectiveMapping(MapType from, MapType to){
		  // can be ignored if we are a rigid rule, without constituent parts
		  // which could be updated to fix the new case
	    }

		public abstract MapType map(MapType from);
	}

	public class Pair<MapType>{
		public MapType from;
		public MapType to;
		public Pair (MapType from, MapType to)
		{
			this.from = from;
			this.to = to;
		}

	}
	public abstract class Strategy<MapType>{
		public abstract Rule<MapType> toRule(IList<Pair<MapType>> pairs);
	}

public abstract class GenMapper<MapType>{
	protected IList<Strategy<MapType>> strategies = new List<Strategy<MapType>>();

	protected Dictionary<MapType,MapType> all = new Dictionary<MapType, MapType>();
	protected IList<Pair<MapType>> unbound = new List<Pair<MapType>>();
	protected Rule<MapType> rule = null;

    public virtual MapType map(MapType from){
	  if( all.ContainsKey(from) ){
		return all[from];
	  }
      if( rule==null && all.Count==1 ){
			MapType key=default(MapType);
			foreach( MapType k in all.Keys ){
				key = k;
			}
			if( ! key.Equals(all[key]) ){
				return all[key];
			}
	  }
      if( rule!=null ){
		return rule.mapChained(from);
	  }
	  return from;
    }

	protected void refilterUnbound(){
		IList<Pair<MapType>> res = new List<Pair<MapType>>();
		foreach(Pair<MapType> p in unbound){
			if( ! rule.mapChained(p.from).Equals(p.to) ){
				res.Add(p);
			}
		}
		unbound = res;
	}
	
	IList<Pair<MapType>> allAsPairList(){
		IList<Pair<MapType>> res = new List<Pair<MapType>>();
		foreach(MapType k in all.Keys ){
			res.Add (new Pair<MapType>(k, all[k]) );
		}
		return res;
	}
	
	public virtual void addMapping(MapType from, MapType to){
      if( ! to.Equals(map(from)) ){
			if( rule!=null ){
				/*
				 try to update existing rule first
					responsibility must be devided between Strategies and existing rules
				*/
				rule.addCorrectiveMapping(from, to);
			}
	  }
			
	  if( ! to.Equals(map(from)) ){ // check again, maybe addCorrectiveMapping() helped
			unbound.Add(new Pair<MapType>(from, to));
			foreach(Strategy<MapType> s in strategies){
				Rule<MapType> r = s.toRule(unbound /* allAsPairList()*/);
				if( r!=null ){
					if( rule!=null ){
						ruleCompose (r);
					}else{
						Console.WriteLine("adding first rule "+r);
 						rule = r; // first rule
					}
					refilterUnbound();
				}
			}
	  }
	  all.Add(from, to);
    }
	
	public override string ToString ()
	{
		return "Mapper1 " + (rule==null ? "null " : rule.ToString());
	}
	
	protected abstract void ruleCompose (Rule<MapType> newRule);

}
